using System;
using System.Collections.Generic;
using System.Text;
using RapidHDL;

namespace RapidHDL
{
    public class UpCounter : Component 
    {

        public NodeVector ResetIn;
		public NodeVector CountOut;
        public NodeVector EnableIn;
        
        private ClockComponent ClockIn;

        int iResetValue;
        int iCountTo;
        int iWidth;
        int iCountBy;
        bool bRisingEdge;

        public UpCounter(Component poParentComponent, string psName,  ClockComponent poClockIn, int piWidth, bool pbRisingEdge, bool pbEnable, int piResetValue, int piCountTo, int piCountBy) 
            : base (poParentComponent,psName)
		{
            if (pbEnable)
                EnableIn = this.CreateNodeVector("ENABLE_I", 1);

            ResetIn = this.CreateNodeVector("RESET_I",1);            
            CountOut = this.CreateNodeVector("COUNT_O", piWidth);

            iWidth = piWidth;
            iResetValue = piResetValue;
            iCountBy = piCountBy;
            bRisingEdge = pbRisingEdge;
            iCountTo = piCountTo;
            //iCountTo = 0;

            ClockIn = poClockIn;
        }

        public override void GenerateStructure()
        {
            string sCountBy = Conversion.IntToBinaryString(iCountBy);
            if (sCountBy.Length > iWidth)
            {
                sCountBy = new string('1',iWidth);
                //should debug assert instead
            }

            string sCountTo = Conversion.IntToBinaryString(iCountTo);

            Constant oCountBy = new Constant(this, "count_by", sCountBy.Length, iCountBy);
            
            RegisterComponent oReg = new RegisterComponent(this, "count_reg",ClockIn,iWidth,bRisingEdge,(EnableIn != null));
            Adder oAdder = new Adder(this, "adder", iWidth,sCountBy.Length, false);
            ConstantSelect oConstantSelect = new ConstantSelect(this, "reset_to", iWidth, iResetValue);
            if (sCountTo != new String('1', iWidth) && Conversion.IntToBinaryString(iCountTo, iWidth) != new String('0', iWidth))
            {
                Constant oCountTo = new Constant(this, "count_to", sCountTo.Length, iCountTo);
                CompareEqual oLimit = new CompareEqual(this, "compare_limit", iWidth, sCountTo.Length);
                oLimit.InputNodesA.Connection  = oReg.OutputNodes;
                oLimit.InputNodesB.Connection  = oCountTo.OutputNodes;
                OrGate oOrReset = new OrGate(this, "reset_or", 2);
                oOrReset.InputNodes[0] = oLimit.Output[0];
                oOrReset.InputNodes[1] = ResetIn[0];
                oConstantSelect.SelIn[0] = oOrReset.OutputNodes[0];
            }
            else
            {
                oConstantSelect.SelIn[0] = ResetIn[0];
            }

            oAdder.InputNodesA.Connection  = oReg.OutputNodes;
            oAdder.InputNodesB.Connection  = oCountBy.OutputNodes;
            oReg.InputNodes.Connection  = oConstantSelect.OutNodes;
            oConstantSelect.InNodes.Connection  = oAdder.OutputNodes;
            CountOut.Connection = oReg.OutputNodes;
            oReg.EnableNodes.Connection = EnableIn;
        }


    }
}
